﻿// XmlVisualizer
// Copyright (C) 2009  Roman Betík - rbetik@gmail.com

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.ComponentModel;
using System.Configuration;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Xml.XPath;
using System.Collections;
using System.Collections.ObjectModel;
using System.Threading;
using System.Reflection;

using XmlVisualizer.Controllers;
using XmlVisualizer.DiagramDesigner;
using XmlVisualizer.DiagramDesigner.Controls;
using Tomers.WPF.Localization;
using XmlVisualizer.Exceptions;
using XmlVisualizer.Models;
using System.Collections.Generic;

namespace XmlVisualizer
{
    /// <summary>
    /// Main window.
    /// </summary>
    public partial class Document : Window, INotifyPropertyChanged
    {
        #region Fields
        private IController controller; // application controller
        private XmlVisualizer.Models.ApplicationModel appModel; // application model
        private String fileName;
        private static String programName = XmlVisualizer.Resources.Strings.ApplicationName;
        private bool wellformed = true;
        private bool empty = true;
        private bool modified = false;
        private XNodeCollection displayedNodes = new XNodeCollection();
        #endregion

        #region Properties
        /// <summary>
        /// Gets the list of displayed nodes.
        /// </summary>
        public XNodeCollection DisplayedNodes
        {
            get { return displayedNodes; }
        }

        /// <summary>
        /// Gets the application model.
        /// </summary>
        public XmlVisualizer.Models.ApplicationModel Model
        {
            get { return appModel; }
        }

        /// <summary>
        /// Gets the controller.
        /// </summary>
        public IController Controller
        {
            get { return controller; }
        }

        /// <summary>
        /// Gets or sets the file name.
        /// </summary>
        public String FileName
        {
            get { return fileName; }
            set
            {
                if (fileName != value)
                {
                    if (!String.IsNullOrEmpty(value))
                    {
                        fileName = value;
                        OnPropertyChanged("FileName");
                    }
                    else
                    {
                        fileName = "Empty";
                        //Exceptions.ErrorHandling.ShowMessage(String.Format("File name {0} does not exist.", value));
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets the window title.
        /// </summary>
        public String WindowTitle
        {
            get { return this.Title; }
            set { this.Title = value; }
        }

        /// <summary>
        /// Gets boolean indicating whether the document has been modified.
        /// </summary>
        public bool Modified
        {
            get { return modified; }
            set { modified = value; OnPropertyChanged("Modified"); }
        }
        #endregion

        private static IController currentController = null;
        public static IController CurrentController
        {
            get { return currentController; }
        }

        private static Document current = null;
        public static Document Current
        {
            get { return current; }
        }

        #region Event handlers
        /// <summary>
        /// Invoked when some property of this document changes.
        /// </summary>
        /// <param name="propertyName">name of the property.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Custom event handler for PropertyChanged event.
        /// </summary>
        /// <param name="sender">The object where the event handler is attached.</param>
        /// <param name="e">The event data.</param>
        void Document_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "FileName")
            {
                SetWindowTitle();
            }
            else if (e.PropertyName == "Modified")
            {
                SetWindowTitle();
                //if (Title != "Empty")
                //	empty = false;
            }
        }

        /// <summary>
        /// Invoked when document is about to be closed.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);
            bool close = SaveDocumentDialog();

            if (!close)
                e.Cancel = true;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            try
            {
                this.PropertyChanged -= new PropertyChangedEventHandler(Document_PropertyChanged);

                DetachEventHandlers();
                appModel.Close();
                empty = true;
                Logging.Log.Stop();
            }
            catch (Exception ex)
            {
                Exceptions.ExceptionHandling.HandleException(ex);
            }
        }

        private bool SaveDocumentDialog()
        {
            bool close = false;
            if (Modified)
            {
                string message = (string)LanguageDictionary.Current.Translate("Document.OnExit.SaveDocument", "Text", null, typeof(string));
                MessageBoxResult res = MessageBox.Show(message, programName, MessageBoxButton.YesNoCancel);
                if (res == MessageBoxResult.Yes)
                {
                    controller.SaveDocument(this.FileName);
                    close = true;
                }
                else if (res == MessageBoxResult.No)
                {
                    close = true;
                }
                else if (res == MessageBoxResult.Cancel)
                {
                    close = false;
                }
            }
            else
            {
                close = true;
            }

            return close;
        }
        #endregion

        #region Events
        /// <summary>
        /// PropertyChanged event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the Document class. Default constructor.
        /// </summary>
        public Document()
        {
            this.Resources = GetResourceDictionary();
            InitializeComponent();
            MakeCommandBindings();

            SetWindowTitle();

            appModel = new XmlVisualizer.Models.ApplicationModel();
            controller = new Controller(appModel);
            currentController = controller;
            current = this;

            this.PropertyChanged += new PropertyChangedEventHandler(Document_PropertyChanged);
            appModel.DocumentClosed += new Action<object, EventArgs>(appModel_DocumentClosed);
            AttachEventHandlers();
        }

        /// <summary>
        /// Initializes a new instance of the Document class.
        /// </summary>
        /// <param name="fileName">file location of the file to display</param>
        public Document(String fileName)
            : this()
        {
            FileName = fileName;
            Load();
        }
        #endregion

        #region Methods
        private void AttachEventHandlers()
        {
            appModel.DocumentChanged += new DocumentChangedDelegate(appModel_DocumentChanged);
            appModel.DocumentLoaded += new Action<object, EventArgs>(appModel_DocumentLoaded);
            appModel.DocumentLoading += new Action<object, EventArgs>(appModel_DocumentLoading);
            appModel.DocumentSaved += new Action<object, EventArgs>(appModel_DocumentSaved);
            appModel.DisplayXml += new Action<object, DisplayXmlEventArgs>(appModel_Show);
            appModel.Shutdown += new Action<object, EventArgs>(appModel_Shutdown);
            appModel.DocumentWellformednessChanged += new Action<object, WellformednessEventArgs>(appModel_DocumentWellformednessChanged);
            appModel.NodeChanged += new Action<object, NodeChangedEventArgs>(appModel_NodeChanged);
            appModel.SelectionChanged += new Action<object, XmlVisualizer.Models.SelectionChangedEventArgs>(appModel_SelectionChanged);
            appModel.ModeChanged += new Action(appModel_ModeChanged);
            appModel.SaveXmv += new Action<object, XmvEventArgs>(appModel_SaveXmv);
            appModel.OpenXmv += new Action<object, XmvEventArgs>(appModel_OpenXmv);
            appModel.DocumentValidityChanged += new Action<object, ValidityEventArgs>(appModel_DocumentValidityChanged);
        }

        private void DetachEventHandlers()
        {
            appModel.DocumentChanged -= new DocumentChangedDelegate(appModel_DocumentChanged);
            appModel.DocumentLoaded -= new Action<object, EventArgs>(appModel_DocumentLoaded);
            appModel.DocumentLoading -= new Action<object, EventArgs>(appModel_DocumentLoading);
            appModel.DocumentSaved -= new Action<object, EventArgs>(appModel_DocumentSaved);
            appModel.DisplayXml -= new Action<object, DisplayXmlEventArgs>(appModel_Show);
            appModel.Shutdown -= new Action<object, EventArgs>(appModel_Shutdown);
            appModel.DocumentWellformednessChanged -= new Action<object, WellformednessEventArgs>(appModel_DocumentWellformednessChanged);
            appModel.NodeChanged -= new Action<object, NodeChangedEventArgs>(appModel_NodeChanged);
            appModel.SelectionChanged -= new Action<object, XmlVisualizer.Models.SelectionChangedEventArgs>(appModel_SelectionChanged);
            appModel.ModeChanged -= new Action(appModel_ModeChanged);
            appModel.SaveXmv -= new Action<object, XmvEventArgs>(appModel_SaveXmv);
            appModel.OpenXmv -= new Action<object, XmvEventArgs>(appModel_OpenXmv);
            appModel.DocumentValidityChanged -= new Action<object, ValidityEventArgs>(appModel_DocumentValidityChanged);
        }

        /// <summary>
        /// Sets window title according to the modified flag.
        /// </summary>
        private void SetWindowTitle()
        {
            if (String.IsNullOrEmpty(FileName))
                FileName = "Empty";

            if (Modified)
            {
                Dispatcher.Invoke((Action)(() =>
                {
                    WindowTitle = FileName + "* - " + programName;
                }));
            }
            else
            {
                Dispatcher.Invoke((Action)(() =>
                {
                    WindowTitle = FileName + " - " + programName;
                }));
            }
        }

        /// <summary>
        /// Resets all neccessary fields and properties.
        /// </summary>
        public void Reset()
        {
            FileName = String.Empty;
            wellformed = true;
            empty = true;
            Modified = false;
            displayedNodes.Clear();
            printingMargins = new System.Drawing.Printing.Margins();
        }

        private void AddDisplayedNode(XNodeInfo node)
        {
            displayedNodes.Add(node.Node, node);
        }

        private void RemoveDisplayedNode(XNodeInfo node)
        {
            if (displayedNodes.ContainsKey(node.Node))
            {
                displayedNodes.Remove(node.Node);
            }
        }

        private ResourceDictionary GetResourceDictionary()
        {
            ResourceDictionary resourceDictionary = null;
            try
            {
                resourceDictionary = new ResourceDictionary();
                String path = XmlVisualizer.Resources.Strings.ResourceDictionaryPath; //System.Configuration.ConfigurationSettings.AppSettings["resourceDictionaryPath"];
                resourceDictionary.Source = new Uri("pack://application:,,,/" + path);
            }
            catch (ArgumentNullException ex)
            {
                ExceptionHandling.HandleException(ex);
            }
            catch (UriFormatException ex)
            {
                ExceptionHandling.HandleException(ex);
            }
            return resourceDictionary;
        }

        /// <summary>
        /// Removes all visual elements from canvas.
        /// </summary>
        public void ClearCanvas()
        {
            Dispatcher.Invoke((Action)(() =>
            {
                designerCanvas.Children.Clear();
            }
            ));
        }

        private void ClearSourceText()
        {
            Dispatcher.Invoke((Action)(() =>
            {
                xmlcode.Text = String.Empty;
            }
            ));
        }

        /// <summary>
        /// Loads the file.
        /// </summary>
        public void Load()
        {
            if (!String.IsNullOrEmpty(FileName))
            {
                Load(FileName);
                empty = false;
            }
            else
            {
                Exceptions.ErrorHandling.ShowMessage("File name for the document was not specified.");
            }
        }

        /// <summary>
        /// Loads the specified file.
        /// </summary>
        /// <param name="filename">file to load</param>
        private void Load(String filename)
        {
            if (!String.IsNullOrEmpty(filename))
            {
                controller.OpenDocument(filename);
            }
            else
            {
                throw new ArgumentNullException("filename");
            }
        }

        /// <summary>
        /// Displays the document's window.
        /// </summary>
        public void ShowDocument()
        {
            Dispatcher.Invoke((Action)(() =>
            {
                try
                {
                    this.Show();
                    this.Activate();
                }
                catch (InvalidOperationException ex)
                {
                    Exceptions.ExceptionHandling.HandleException(ex);
                }
            }
            ));
        }
        #endregion

        private void zoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (e.OldValue != 0)
            {
                svCanvas.ScrollToHorizontalOffset((svCanvas.HorizontalOffset / e.OldValue) * e.NewValue);
                svCanvas.ScrollToVerticalOffset((svCanvas.VerticalOffset / e.OldValue) * e.NewValue);
            }
            else
            {
                svCanvas.ScrollToHorizontalOffset(svCanvas.HorizontalOffset * e.NewValue);
                svCanvas.ScrollToVerticalOffset(svCanvas.VerticalOffset * e.NewValue);
            }
        }

        private void cb_XPath_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                MyCommands.XPathSelection.Execute(sender, btn_XPath);
            }
        }
    }
}